package com.example.hotdeploy.hot;

import java.io.*;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description: 需要热替换的服务类工厂
 * @Author: kai.yang
 * @Date: 2019-03-10 07:13
 */
public class HotDeployFactory {

    /**
     * 正在使用的classloader
     */
    private static ClassLoader currentClassLoader;

    private static String[] needHotClasses;

    private static AtomicBoolean inited = new AtomicBoolean(false);


    /**
     * 默认加载根目录
     */
    private static String javaRoot = "/Users/kai.yang/Desktop/java-test/hot/";


    public static void init(String... needHotClasses) {
        HotDeployFactory.inited.compareAndSet(false, true);
        HotDeployFactory.currentClassLoader = new hotClassLoader(javaRoot, needHotClasses);
        HotDeployFactory.needHotClasses = needHotClasses;
    }

    public static void upgrade() {
        if (inited.get()){
            HotDeployFactory.currentClassLoader = new hotClassLoader(HotDeployFactory.javaRoot, needHotClasses);
        }else{
            throw new RuntimeException("非法异常");
        }
    }

    public static Object getObject(String className) throws Exception {
        Class<?> aClass = currentClassLoader.loadClass(className);
        return aClass.newInstance();
    }

    /**
     * 自定义classloader类
     */
    static class hotClassLoader extends ClassLoader {

        private String baseDir;

        private HashSet<String> myClasses = new HashSet<>();

        public hotClassLoader(String baseDir, String[] classNames) {
            super(null);
            this.baseDir = baseDir;
            loadDir(classNames);
        }


        private void loadDir(String[] classNames) {
            if (classNames == null || classNames.length == 0) {
                return;
            } else {
                for (String clazz : classNames) {
                    try {
                        StringBuilder sb = new StringBuilder(baseDir);
                        sb.append(clazz.replace(".", File.separator)).append(".class");
                        loadClazz(clazz, new File(sb.toString()));
                        myClasses.add(clazz);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }


        private Class loadClazz(String name, File file) throws IOException {
            InputStream inputStream = new FileInputStream(file);
            byte[] raw = new byte[(int) file.length()];
            inputStream.read(raw);
            return defineClass(name, raw, 0, (int) file.length());
        }


    }


}
